package com.zhou.core.logana;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zhou.ConsoleSystem;
import com.zhou.ToolsApp;
import com.zhou.core.os.v1.OSService;
import com.zhou.core.redis.RedisSessionService;
import com.zhou.core.logana.handle.ILogAnaHandler;
import com.zhou.core.logana.handle.LogAnaHandlerProxy;
import com.zhou.core.logana.repository.ILogAnaRepository;
import com.zhou.core.logana.repository.LogAnaRepositoryFactory;
import com.zhou.util.PropertyFileReader;
import com.zhou.util.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Created by zhouyongjun on 2020/6/12
 */
public class LogAnaService  implements ConsoleSystem {
    static LogAnaService instance = new LogAnaService();

    private static final Logger logger = LoggerFactory.getLogger(LogAnaService.class);
    private static final Logger saveLogger = LoggerFactory.getLogger("logana");
    private static final Logger unusualLog = LoggerFactory.getLogger("logana");

    boolean isStartUp;
    LogAnaConfig config;
    ScheduledThreadPoolExecutor pool;
    ScheduledThreadPoolExecutor unusualPool;
    Map<String, LogAnaHandlerProxy> recordMap = new HashMap<>();
    ILogAnaRepository saveRepository;
    ILogAnaRepository readRepository;
    private  LogAnaService(){}
    @Override
    public ToolsApp.SystemType getConsoleSubSystemType() {
        return ToolsApp.SystemType.LOGANA;
    }

    @Override
    public void startup(Object... params) throws Throwable {
        logger.info("startup...");
        isStartUp = true;
        initConfig();
        initRepository();
        initRegisterHandlers();
        initPool();
        if (params != null && params.length > 0 && params[0] != null)
        {
            String extra = params[0].toString();
            config.setExtraRepositoryKey(extra);
        }
    }

    public void initRepository() throws Exception {
        saveRepository = LogAnaRepositoryFactory.getInstance().createRepository(config.getSaveRepositoryType());
        readRepository = LogAnaRepositoryFactory.getInstance().createRepository(config.getReadRepositoryType());
        logger.info("initRepository...saveRepository="+saveRepository+",readRepository="+readRepository);
    }

    public void initRegisterHandlers() {
        logger.info("initRegisterHandlers...");
//        registerHandler(CpuJsonLogAnaHandlerImpl.class);
//        registerHandler(GCJsonLogAnaHandlerImpl.class);
//        registerHandler(LoadJsonLogAnaHandlerImpl.class);
//        registerHandler(MemoryJsonLogAnaHandlerImpl.class);
//        registerHandler(ThreadNumJsonLogAnaHandlerImpl.class);
        for (String clazz : config.getHandlerClasses())
        {
            try {
                Class<? extends ILogAnaHandler>  handlerClazz =  (Class<? extends ILogAnaHandler>)Class.forName(clazz);
                registerHandler(handlerClazz);
            } catch (Exception e) {
              logger.error("error : clazz="+clazz ,e);
            }
        }
    }

    private void registerHandler(Class<? extends ILogAnaHandler> clazz) {
        LogAnaHandlerProxy proxy = new LogAnaHandlerProxy();
        proxy.setRecordClass(clazz);
        recordMap.put(clazz.getSimpleName(),proxy);
        logger.info("registerRecord clazz=" + clazz.getName());
    }

    private void initPool() {
        logger.info("initPool...");
        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2
                , new ThreadFactoryBuilder().setNameFormat("logana-%d").build());
        unusualPool= (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5
                , new ThreadFactoryBuilder().setNameFormat("logana-unusual-%d").build());
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    if (!isStartUp ) return;
                    if (!config.isSaveSwitchOpen()) return;
                    logger.debug("pool run... isStartUp="+isStartUp+",config.isReadSwitchOpen()="+config.isSaveSwitchOpen());
                    List<LogRecord> records = new ArrayList<>();
                    for (LogAnaHandlerProxy proxy : recordMap.values())
                    {
                        try {
                            LogRecord record = proxy.tick();
                            if (record == null) continue;
                            records.add(record);
                            String assembleRepositoryKey = assembleWriteRepositoryKey(record.getRepositoryKey());
                            LogAnaService.getInstance().logToFile(assembleRepositoryKey,record.getValue());
                        } catch (Exception e) {
                            logger.error("error :" + proxy.getRecordClass().getName(),e);
                        }
                    }
                    if (saveRepository != null)
                    {
                        saveRepository.saveToList(records.toArray(new LogRecord[records.size()]));
                    }

                } catch (Throwable e) {
                   logger.error("",e);
                }
            }
        }, config.getTickPeriodSeconds(), config.getTickPeriodSeconds(), TimeUnit.SECONDS);
    }

    private void initConfig() throws Throwable {
        config = new LogAnaConfig();
        config.load();
    }

    @Override
    public void shutdown(Object... params) throws Throwable {
        isStartUp = false;
        pool.shutdown();
        unusualPool.shutdown();
        RedisSessionService.getInstance().shutdown();
    }

    @Override
    public boolean isStartUp() {
        return isStartUp;
    }

    public static LogAnaService getInstance() {
        return instance;
    }

    public void setStartUp(boolean startUp) {
        isStartUp = startUp;
    }

    public LogAnaConfig getConfig() {
        return config;
    }

    public void setConfig(LogAnaConfig config) {
        this.config = config;
    }

    public ScheduledThreadPoolExecutor getPool() {
        return pool;
    }

    public void setPool(ScheduledThreadPoolExecutor pool) {
        this.pool = pool;
    }

    public Map<String, LogAnaHandlerProxy> getRecordMap() {
        return recordMap;
    }

    public void setRecordMap(Map<String, LogAnaHandlerProxy> recordMap) {
        this.recordMap = recordMap;
    }

    public ScheduledThreadPoolExecutor getUnusualPool() {
        return unusualPool;
    }

    public void setUnusualPool(ScheduledThreadPoolExecutor unusualPool) {
        this.unusualPool = unusualPool;
    }

    public ILogAnaRepository getSaveRepository() {
        return saveRepository;
    }

    public void setSaveRepository(ILogAnaRepository saveRepository) {
        this.saveRepository = saveRepository;
    }

    public ILogAnaRepository getReadRepository() {
        return readRepository;
    }

    public void setReadRepository(ILogAnaRepository readRepository) {
        this.readRepository = readRepository;
    }

    /**
    记录到文件中
     * @param key
     * @param msg
     */
    public void logToFile(String key,String msg)
    {
        try {
            saveLogger.info("logToFile : size="+msg.length() + key+LoganaConst.REGEX+msg);
        } catch (Exception e) {
            logger.error("key="+key+"j,value="+msg,e);
        }
    }

    /**
     * ���ɱ����KEY
     * @param repositoryKey
     * @return
     */
    public  String assembleWriteRepositoryKey(String repositoryKey)
    {
        StringBuilder sb = new StringBuilder(repositoryKey).append("_")
                .append(getConfig().getExtraRepositoryKey()).append("_").append(TimeUtils.formatDateNoSpr(TimeUtils.now()));
        return sb.toString();

    }

    /**
     * ���ɶ�ȡ��KEY
     * @param repositoryKey
     * @param extraKey
     * @param formatDateNoSpr
     * @return
     */
    public  String assembleReadRepositoryKey(String repositoryKey,String extraKey,String formatDateNoSpr)
    {
        StringBuilder sb = new StringBuilder(repositoryKey).append("_")
                .append(extraKey).append("_").append(formatDateNoSpr);
        return sb.toString();
    }

    public void unusual()
    {
        long time  = TimeUtils.nowLong();
        logger.info("unusual execute begin ...");
        try {
            if (OSService.isWinSystem())
            {
                return;
            }

            String path = "./logana/temp";
            File parentFile = new File(path);
            if (!parentFile.exists())
            {
                parentFile.mkdirs();
            }
            int pid = OSService.getSelfProcessID();
            String formatTime =  TimeUtils.formatNoSpr(TimeUtils.now());
            String[] cmds =
            {
                    String.format("top > %s/top_%s.log",path,formatTime),
                    String.format("top -p %d -H > %s/top_H_%d_%s.log ",pid,path,pid,formatTime),
                    String.format("free -m  > %s/free_m_%s.log ",path,formatTime),
                    String.format("jstack -l %d > %s/jstack_%d_%s.log",pid,path,pid,formatTime),
                    String.format("jstat -gc %d >> %s/jstat_%d_%s.log  && jstat -gcutil %d >> %s/jstat_%d_%s.log",pid,path,pid,formatTime,
                     pid,path,pid,formatTime),
                    String.format("vmstat > %s/vmstat_%s.log",path,formatTime),
            };
            for (final String cmd : cmds)
            {
                logger.info("cmd["+cmd+"] execute begin...");
                long cmdTime = TimeUtils.nowLong();
                Callable<String> call = new Callable<String>() {
                    public String call() throws Exception {
                        List<String> list = OSService.cmd(cmd);
                        String result = JSON.toJSONString(list);
                        logger.debug("cmd="+cmd+", result:"+ result);
                        return result;
                    }
                };
                Future<String> future = unusualPool.submit(call);
                try {
                    future.get(10,TimeUnit.SECONDS);
                }  catch (Exception e) {
                    logger.error("error : timeout cmd ="+cmd,e);
                }finally {
                    logger.info("cmd["+cmd+"] execute end... cost:"+(TimeUtils.nowLong() - cmdTime));
                }
            }
        } catch (Exception e) {
            logger.error("",e);
        }finally {
            logger.info("unusual execute end ...cost:"+(TimeUtils.nowLong() - time));
        }

    }


    public List<String>  readFromList(String repositoryKey,String extraKey,String formatDateNoSpr, int fromIndex,int toIndex)
    {
        LogAnaResult result = readRepository.readFromList(repositoryKey,extraKey,formatDateNoSpr,fromIndex,toIndex);
        if (result.isSucc()) return result.getData();
        return new ArrayList<>();
    }

    public List<String>  readFromList(String repositoryPoolName, PropertyFileReader propertyFileReader, String repositoryKey, String extraKey, String formatDateNoSpr, int fromIndex, int toIndex)
    {
        LogAnaResult result = readRepository.readFromList(repositoryPoolName,propertyFileReader,repositoryKey,extraKey,formatDateNoSpr,fromIndex,toIndex);
        if (result.isSucc()) return result.getData();
        return new ArrayList<>();
    }

}
